#ifndef INSTANCE_H
#define INSTANCE_H


#include "user.h"
#include "MsgModPC.h"
#include "TLVPacketer.h"
#include "Packet.h"


class CGameProcess
{
public:
	virtual void GameStart(int playernum) {};
	virtual bool PlayerInput(int which, const DataFlow &data) { return 1;};
	TLVpacketer *PlayerPacket;

	CGameProcess() : PlayerPacket(0) {}
	~CGameProcess() { if (PlayerPacket) delete [] PlayerPacket; }
};

class CGameInstance : public CMsgModPC
{
public:
	typedef struct {
		CUser::T_UserUniqueID	 id;
		int	roomid;
		int gameid;
	} T_UserInfo;
	bool GameStart;

	typedef string T_InstanceID;
	typedef map<T_ModuleDescriptor, T_UserInfo> T_UserRoomMap;
	T_InstanceID ID;
	CGameProcess *Game;
	TLVpacketer Back;

	T_ModuleDescriptor Host;
	T_UserRoomMap Users;

public:
	class CMsgSocket *Broad;
	T_ModuleDescriptor Hall;
	
	~CGameInstance() { delete Game; }
	void SetRoom(int roomid, string hostname, unsigned char current, unsigned char total);
	//void StartInstance();
	void SendPacket(T_ModuleDescriptor md)
	{
		for (int i = 0; i < PlayerNumber + 1; i++)
		{
			Game->PlayerPacket[i].MakePacket();
			Back[P_GAME_PLAYER0 + i] = Game->PlayerPacket[i];
		}
		Back[P_SERVER_COMMAND] = P_SERVER_COMMAND_ROOM;
		Back.MakePacket();

		for (T_UserRoomMap::iterator it = Users.begin(); it != Users.end(); it++)
		{
			T_ModuleDescriptor sendmd = it->first;
			POST_MSG_TO(sendmd, MSG_GAME_PACKET, GetMD(), Back );
		}
		//POST_MSG_TO(md, MSG_GAME_PACKET, GetMD(), Back );
		Back.clear();
		for (int i = 0; i < PlayerNumber + 1; i++)
		{
			Game->PlayerPacket[i].clear();
		}
	}
	void StartGame()
	{		
		Game->PlayerPacket = new TLVpacketer[PlayerNumber + 1];
		int g = 0;
		for ( int r = 0; r < PlayerNumber; r++ )
		{
			if ( RoomID[r] >= 0 )
			{
				GameID[g++] = RoomID[r];
			}
		}
		AllocateRandomGameID(PlayerNumber);
		for ( int r = 0; r < PlayerNumber; r++ )
		{
			Users [ GameID[r] ] .gameid = r;
		}
		char *m = Back[ P_ROOM_GAME_MAP ].ReSet( PlayerNumber );
		for ( int r = 0; r < PlayerNumber; r++ )
		{
			m[r] = (char) Users [ GameID[r] ] .roomid;
		}

		Back[P_ROOM] = P_ROOM_GAME_START;
		Back[P_ROOM_PLAYERNUMBER] = PlayerNumber;
		Game->GameStart(PlayerNumber);
	}

public:
	virtual void MOnRecvMsg(unsigned int type, T_LParameter lp, const DataFlow &data)
	{
		bool ishost = 0;
		string retmsg;
		int ret;
		T_ModuleDescriptor md;
		T_UserInfo user;
		TLVpacketer proom, pmap, coming;

		user.roomid = -1;

		if ( RoomClosed ) return;

		switch (type)
		{
		case MSG_PLAYER_HOST_JOIN:
			ishost = 1;
		case MSG_PLAYER_JOIN:
			md = lp;
			user.id = string( data.GetData() );
			if (ishost) Host = md;
			if ( MAP_NOT_CONTAIN(Users, md) )
			{
				ret = AllocateFreePlayer(md);

				if (ret != -1)
				{
					RoomID[ret] = md;
					PlayerNumber++;

					proom[P_ROOM_PLAYER_ID] = ret;
					user.roomid = ret;
					Users[md] = user;
					for (T_UserRoomMap::iterator it = Users.begin(); it != Users.end(); it++)
					{
						pmap[it->second.roomid] = it->second.id;
					}
					pmap.MakePacket();
					proom[P_ROOM_PLAYER_MAP] = pmap;

					for (T_UserRoomMap::iterator it = Users.begin(); it != Users.end(); it++)
					{
						T_ModuleDescriptor sendmd = it->first;
						if (sendmd == md)
						{
							if ( ishost ) proom[P_SERVER_COMMAND] = P_SERVER_COMMAND_CREATE_ACCEPT;
								else proom[P_SERVER_COMMAND] = P_SERVER_COMMAND_JOIN_ACCEPT;
						}
						else proom[P_SERVER_COMMAND] = P_SERVER_COMMAND_ROOM;
						proom.MakePacket();
						POST_MSG_TO(sendmd, MSG_GAME_PACKET, GetMD(), proom );
					}
					SetRoom( GetMD(), Users[Host].id, PlayerNumber, 5);
					POST_MSG_TO(md, MSG_PLAYER_JOIN_ACCEPT, GetMD(), ret );
				}
				else
					POST_MSG_TO(md, MSG_PLAYER_JOIN_DENY, GetMD(), null );
			}
			break;
		case MSG_PLAYER_EXIT:
			md = lp;
			if ( MAP_NOT_CONTAIN(Users, md) ) return;
			if (md != Host)
			{
				if ( Users[md].roomid >= 0 ) 
				{
					RoomID[ Users[md].roomid ] = -1;
					PlayerNumber--;
					SetRoom( GetMD(), Users[Host].id, PlayerNumber, 5);
				}
				Users.erase( md );
			}
			else SetRoom( GetMD(), Users[Host].id, 0, 0);

			if (md == Host)
			{
				proom[P_ROOM] = P_ROOM_DELETE;
			}
			proom[P_SERVER_COMMAND] = P_SERVER_COMMAND_ROOM;
			if (md != Host)
			{
				for (T_UserRoomMap::iterator it = Users.begin(); it != Users.end(); it++)
				{
					pmap[it->second.roomid] = it->second.id;
				}
				pmap.MakePacket();
				proom[P_ROOM_PLAYER_MAP] = pmap;
			}
			proom.MakePacket();
			for (T_UserRoomMap::iterator it = Users.begin(); it != Users.end(); it++)
			{
				T_ModuleDescriptor sendmd = it->first;
				if (sendmd != md)
					POST_MSG_TO(sendmd, MSG_GAME_PACKET, GetMD(), proom );
			}

			if (md == Host)
			{
				RoomClosed = 1;
				POST_MSG_TO (Hall, MSG_ROOM_DELETE, GetMD(), null );
			}
			
			break;
		case MSG_ROOM_MSG:
			md = lp;
			if ( MAP_NOT_CONTAIN(Users, md) ) return;
			coming = data;
			proom[P_SERVER_COMMAND] = P_SERVER_COMMAND_ROOM;
			proom[P_ROOM_MSG] = coming[P_USER_ROOM_MSG];
			proom[P_ROOM_MSG_ID] = Users[md].roomid;
			proom.MakePacket();
			for (T_UserRoomMap::iterator it = Users.begin(); it != Users.end(); it++)
			{
				T_ModuleDescriptor sendmd = it->first;
				POST_MSG_TO(sendmd, MSG_GAME_PACKET, GetMD(), proom );
			}
			break;
		case MSG_GAME_START:
			md = lp;
			if ( (!GameStart) && (md == Host) )
			{
				if (PlayerNumber <= 2)
				{
					retmsg = "Not enough player";
					POST_MSG_TO(md, MSG_GAME_START_DENY, GetMD(), retmsg );
					break;
				}
				POST_MSG_TO(md, MSG_GAME_START_ACCEPT, GetMD(), null );
				GameStart = 1;
				StartGame();
				SendPacket(md);
			}
			break;
		case MSG_GAME_COMMAND:
			md = lp;

			if ( GameStart && (!MAP_NOT_CONTAIN(Users, md)) )
			{
				user = Users[md];
 				if ( Game->PlayerInput(user.gameid, data) )   //available input
					SendPacket(md);
			}
			break;
		}
	}

	int AllocateFreePlayer(T_ModuleDescriptor user)
	{
		for (int i = 0; i < MaxPlayer; i++ )
		{
			if(RoomID[i] == -1)
			{
				return i;
			}
		}
		return -1;
	}
	void AllocateRandomGameID(int total)
	{
		srand( (unsigned)time(0) );

		T_ModuleDescriptor t;
		int ran;
		while ( total > 1 )
		{
			total--;
			ran = BETWEEN(0, total) ;
			t = GameID[total];
			GameID[total] = GameID[ ran ];
			GameID[ ran ] = t;
		}
	}

	CGameInstance(CGameProcess *g) : GameStart(0) , PlayerNumber(0) , RoomClosed(0)
	{
		Game = g;
		for (int i = 0; i < MaxPlayer; i++ )
		{
			RoomID[i] = -1;
		}
	}

private:
	int	PlayerNumber;
	T_ModuleDescriptor	RoomID[MaxPlayer];
	T_ModuleDescriptor	GameID[MaxPlayer];
	bool RoomClosed;
};

#endif